//Copyright 2008 Kristian Gundry And Rune Juhl-Petersen
//This file is part of Plain.

//Plain is free software: you can redistribute it and/or modify
//it under the terms of the GNU Lesser General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//any later version.

//Plain is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public License
//along with Plain.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using Plain.Search;
using NHibernate;
using Plain.Utilities;

using helper = NHibernate.Helper;

namespace Plain.Data.NHibernate
{
	public class NHibernateSearchParser
	{
        IDictionary<string, object> parameters = null;
        IDictionary<string, object> hqlparameters = null;
        public NHibernateSearchParser(IDictionary<string, object> dic) { 
        }
        /// <summary>
        /// Returns a NHibernate query
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="crit"></param>
        /// <returns></returns>
		public IQuery ParseCriteria<T>(Criteria crit) {
			return helper.Broker.Session.CreateQuery("select distinct o From " + typeof(T).Name + " as o where " + hql(crit));
		}
        public IQuery ParseCriteria<T>(Criteria crit, string order) {
            return helper.Broker.Session.CreateQuery("select distinct o From " + typeof(T).Name + " as o where " + hql(crit) + (!string.IsNullOrEmpty(order)?" order by " + order : ""));
        }
        public IQuery ParseCriteria<T>(Criteria crit, string order,IDictionary<string,object> param) {
            parameters = param;
            hqlparameters = new Dictionary<string, object>();
            return helper.Broker.Session.CreateQuery("select distinct o From " + typeof(T).Name + " as o where " + hql(crit) + (!string.IsNullOrEmpty(order) ? " order by " + order : ""));

        }

        public IQuery ParseCriteriaCount<T>(Criteria crit) {
            return helper.Broker.Session.CreateQuery("select count() from (select distinct o From " + typeof(T).Name + " as o where " + hql(crit) + ")");
        }
		public string hql(Criteria crit) {
			CriteriaCollection col = crit as CriteriaCollection;
			if (col != null)
			{
				return hql(col);
			}
			else {
				return handleCriteria(crit);		
			}
		}

		private string hql(CriteriaCollection col) {
			StringBuilder sb = new StringBuilder();
			foreach (Criteria crit in col.Criterias) {
				string r = hql(crit);
				if (!string.IsNullOrEmpty(r)) {
					if (sb.Length > 0) {
						sb.Append(" " + parseCollectionType(col.Type) +" ");
					}
					sb.Append(r);
				}
			}
			if (!string.IsNullOrEmpty(sb.ToString()))
			{
				if (col.Type == CriteriaCollectionType.OneOf){
					return "(" + sb.ToString() + ")";
				}else{
					return sb.ToString();
				}
			}
			else return null;                                                                           
		}

		private string parseCollectionType(CriteriaCollectionType type){
			switch(type){
				case CriteriaCollectionType.OneOf:
					return "or";
					
				default:
					return "and";
			}
		}

		private  string handleCriteria(Criteria crit){
			SimpleCriteria simp = crit as SimpleCriteria;
			if (simp != null)
			{
				string c;
                string v;
				switch (simp.Type)
				{
					case SimpleCriteriaType.Equal:
						c = "{0}={1}";
                        v = "'{0}'";
						break;
					case SimpleCriteriaType.GreaterThan:
						c = "{0}>{1}";
                        v = "'{0}'";
						break;
					case SimpleCriteriaType.LessThan:
						c = "{0}<{1}";
                        v = "'{0}'";
						break;
					case SimpleCriteriaType.GreaterThanOrEqual:
                        v = "'{0}'";
						c = "{0}>={1}";
						break;
					case SimpleCriteriaType.LessThanOrEqual:
						c = "{0}<={1}";
                        v = "'{0}'";
						break;
					case SimpleCriteriaType.NotEqual:
						c = "{0}!={1}";
                        v = "'{0}'";
						break;
                    case SimpleCriteriaType.Contains:
                        c = "{0} like {1}";
                        v = "'%{0}%'";
                        break;
                    case SimpleCriteriaType.StartsWith:
                        c = "{0} like {1}";
                        v = "'{0}%'";
                        break;
                    case SimpleCriteriaType.EndsWith:
                        c = "{0} like {1}";
                        v = "'%{0}'";
                        break;
					default:
						c = "0";
                        v = "";
						break;
				}
                if (parameters != null && parameters.ContainsKey(simp.Id.Name)) {
                    //parameter is included

                    //hqlparameters.Add(simp.Id.Name, parameters[simp.Id.Name]);
                    
                    return string.Format(c, "o." + simp.Id.Name, ":" + simp.Id.Name);
                } else {
                    return string.Format(c, "o." + simp.Id.Name, string.Format(v, simp.Payload.ToString()));
                }
            } else if (crit is InCollectionCriteria) {
                InCollectionCriteria col = (InCollectionCriteria)crit;
                string values= col.Concat<object>("','");
                if (values.Length > 0) {
                    return col.Id.Name + " in ('" + values + "')";
                } else return "1=0";
                
            } else throw new Exception("Unknown criteria type");
		}
	}
}

